/*
 *  AS IS License 
 * ==Naive Coders==
 */
package com.naivecoders.refex;

import java.util.ArrayDeque;
import java.util.Deque;

import com.naivecoders.refex.functions.AbstractFunction;

/**
 *
 * @author andrew
 */
public class Compiler extends Tokenizer {

    private final Namespace namespace;
    private final FunctionFactory factory;
    private final Deque<FunctionBuilder> builderStack = new ArrayDeque<>();

    public Compiler(Namespace namespace, FunctionFactory ff) {
        this.namespace = namespace;
        factory = ff;
    }

    @Override
    protected void onProc() throws IllegalSyntaxException {
        final FName currFuncName = copyLabel();
        if (!builderStack.isEmpty()) {
            throwIllegalSyntax("Function %s is defined within body of %s",
                    (currFuncName), (builderStack.peekFirst().getDeclaredName()));
        }
        onList();
        builderStack.peek().setDeclaredName(currFuncName);
    }

    @Override
    protected void onProcEnd() throws IllegalSyntaxException {
        if (builderStack.size() != 1) {
            throwIllegalSyntax("Uexpected function end %s",
                    (builderStack.peekFirst().getDeclaredName()));

        }
        final FunctionBuilder fb = builderStack.pop();
        try {
            namespace.define(fb.getDeclaredName(), fb.build());
        } catch (InternalTypeCheckException ex) {
            throwIllegalSyntax(ex);
        }
    }

    @Override
    protected void onList() throws IllegalSyntaxException {
        final FunctionBuilder fb = new FunctionBuilder(factory);
        builderStack.push(fb);
    }

    @Override
    protected void onListEnd() throws IllegalSyntaxException {
        try {
            final AbstractFunction af = builderStack.pop().build();
            builderStack.peek().add(af);
        } catch (InternalTypeCheckException ex) {
            throwIllegalSyntax(ex);
        }
    }

    @Override
    protected void onLabel() throws IllegalSyntaxException {
        final FName lbl = copyLabel();

        try {
            builderStack.peek().add(lbl);
        } catch (InternalTypeCheckException e) {
            throwIllegalSyntax(e);
        }
    }


    @Override
    protected void onCall() throws IllegalSyntaxException {
        final FunctionBuilder fb = new InvocationBuilder(factory);
        builderStack.push(fb);
    }

    @Override
    protected void onCallEnd() throws IllegalSyntaxException {
        onListEnd();
    }
}
